﻿--Macaroon, a World of Warcraft® user interface addon.
--Copyright© 2008 Connor H. Chenoweth, aka Maul

Macaroon.BarIndex = {}

Macaroon.BarIndexByName = {}

Macaroon.ButtonBars = {}

Macaroon.CreateBarTypes = {}

local autoHideIndex, alphaupIndex, anchorIndex = {}, {}, {}

local ss, css, managedStates, playerEnteredWorld, meetingStoneChanged, shared, temp

local stratas = { "BACKGROUND", "LOW", "MEDIUM", "HIGH", "DIALOG", "TOOLTIP" }

local alphaUps = {
	MACAROON_STRINGS.ALPHAUP_NONE,
	MACAROON_STRINGS.ALPHAUP_BATTLE,
	MACAROON_STRINGS.ALPHAUP_MOUSEOVER,
	MACAROON_STRINGS.ALPHAUP_BATTLEMOUSE,
	MACAROON_STRINGS.ALPHAUP_RETREAT,
	MACAROON_STRINGS.ALPHAUP_RETREATMOUSE,
}

local notSharedButtonData = {
	action = true,
	spell = true,
	type = true,
	macro = true,
	macroIcon = true,
	macroName = true,
	macroNote = true,
	macroUseNote = true,
	macroAuto = true,
	macroRand = true,
	upClicks = true,
	downClicks = true,
	spellCounts = true,
	comboCounts = true,
}

local gsub = string.gsub
local find = string.find
local match = string.match
local gmatch = string.gmatch
local format = string.format
local lower = string.lower
local upper = string.upper
local floor = math.floor
local ceil = math.ceil
local mod = mod

local MouseIsOver = _G.MouseIsOver
local InCombatLockdown = _G.InCombatLockdown

local MACAROON_STRINGS = MACAROON_STRINGS
local copyTable = Macaroon.copyTable
local clearTable = Macaroon.clearTable

local function control_OnUpdate(self, elapsed)

	for k,v in pairs(autoHideIndex) do
		if (v~=nil) then
			if (MouseIsOver(k)) then
				if (v:GetAlpha() < k.config.alpha) then
					if (v:GetAlpha()+ss.fadeSpeed <= 1) then
						v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
					else
						v:SetAlpha(1)
					end
				else
					k.vis = 1;
				end
			end

			if (not MouseIsOver(k)) then
				if (v:GetAlpha() > 0) then
					if (v:GetAlpha()-ss.fadeSpeed >= 0) then
						v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
					else
						v:SetAlpha(0)
					end
				else
					k.vis = 0;
				end
			end
		end
	end

	for k,v in pairs(alphaupIndex) do
		if (v~=nil) then
			if (k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_BATTLE or k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_BATTLEMOUSE) then

				if (InCombatLockdown()) then

					if (v:GetAlpha() < 1) then
						if (v:GetAlpha()+ss.fadeSpeed <= 1) then
							v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
						else
							v:SetAlpha(1)
						end
					else
						k.vis = 1;
					end

				else
					if (k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_BATTLEMOUSE) then

						if (MouseIsOver(k)) then
							if (v:GetAlpha() < 1) then
								if (v:GetAlpha()+ss.fadeSpeed <= 1) then
									v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
								else
									v:SetAlpha(1)
								end
							else
								k.vis = 1;
							end
						else
							if (v:GetAlpha() > k.config.alpha) then
								if (v:GetAlpha()-ss.fadeSpeed >= 0) then
									v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
								else
									v:SetAlpha(k.config.alpha)
								end
							else
								k.vis = 0;
							end
						end
					else
						if (v:GetAlpha() > k.config.alpha) then
							if (v:GetAlpha()-ss.fadeSpeed >= 0) then
								v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
							else
								v:SetAlpha(k.config.alpha)
							end
						else
							k.vis = 0;
						end
					end
				end

			elseif (k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_RETREAT or k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_RETREATMOUSE) then

				if (not InCombatLockdown()) then

					if (v:GetAlpha() < 1) then
						if (v:GetAlpha()+ss.fadeSpeed <= 1) then
							v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
						else
							v:SetAlpha(1)
						end
					else
						k.vis = 1;
					end

				else
					if (k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_RETREATMOUSE) then

						if (MouseIsOver(k)) then
							if (v:GetAlpha() < 1) then
								if (v:GetAlpha()+ss.fadeSpeed <= 1) then
									v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
								else
									v:SetAlpha(1)
								end
							else
								k.vis = 1;
							end
						else
							if (v:GetAlpha() > k.config.alpha) then
								if (v:GetAlpha()-ss.fadeSpeed >= 0) then
									v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
								else
									v:SetAlpha(k.config.alpha)
								end
							else
								k.vis = 0;
							end
						end
					else
						if (v:GetAlpha() > k.config.alpha) then
							if (v:GetAlpha()-ss.fadeSpeed >= 0) then
								v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
							else
								v:SetAlpha(k.config.alpha)
							end
						else
							k.vis = 0;
						end
					end
				end

			elseif (k.config.alphaUp == MACAROON_STRINGS.ALPHAUP_MOUSEOVER) then

				if (MouseIsOver(k)) then
					if (v:GetAlpha() < 1) then
						if (v:GetAlpha()+ss.fadeSpeed <= 1) then
							v:SetAlpha(v:GetAlpha()+ss.fadeSpeed)
						else
							v:SetAlpha(1)
						end
					else
						k.vis = 1;
					end
				else
					if (v:GetAlpha() > k.config.alpha) then
						if (v:GetAlpha()-ss.fadeSpeed >= 0) then
							v:SetAlpha(v:GetAlpha()-ss.fadeSpeed)
						else
							v:SetAlpha(k.config.alpha)
						end
					else
						k.vis = 0;
					end
				end
			end
		end
	end
end

local onUpdate = MacaroonControl:GetScript("OnUpdate")
MacaroonControl:SetScript("OnUpdate", function(self, elapsed) onUpdate(self, elapsed) control_OnUpdate(self, elapsed) end)

local function createAnchor(bar)

	local index = #anchorIndex + 1
	local anchor = CreateFrame("Frame", "MacaroonAnchor"..index, bar)

	anchor:SetID(index)
	anchor:SetWidth(2)
	anchor:SetHeight(2)
	anchor:SetMovable(true)

	anchorIndex[index] = { anchor, 0 }

	return anchor
end

local function getAnchor(bar, state)

	if (bar[state] and bar[state].anchor) then
		return bar[state].anchor
	end

	local anchor

	for k,v in pairs(anchorIndex) do
		if (v[2] == 1) then
			anchor = v[1]
		end
	end

	if (not anchor) then
		anchor = createAnchor(bar)
	end

	anchorIndex[anchor:GetID()][2] = 0

	anchor:SetPoint("CENTER", bar, "CENTER")

	bar[state].anchor = anchor

	return anchor
end

local function releaseAnchor(bar, state)

	local anchor = bar[state].anchor

	if (anchor) then

		anchorIndex[anchor:GetID()][2] = 1

		anchor:ClearAllPoints()

		bar[state].anchor = nil

	end
end

local function setDefaultPosition(bar, y)

	local barHeight = 40

	if (not y) then	y = 90 end

	bar:SetUserPlaced(false)
	bar:ClearAllPoints()
	bar:SetPoint("CENTER", "UIParent", "BOTTOM", 0, y)
	bar.config.point, bar.config.x, bar.config.y = Macaroon.GetPosition(bar)
	bar:SetUserPlaced(true)

	y = y + barHeight

	return y
end

local function setPosition(bar)

	if (bar.config.snapToPoint and bar.config.snapToFrame) then
		Macaroon.SnapTo.StickToPoint(bar, _G[bar.config.snapToFrame], bar.config.snapToPoint, bar.config.padH, bar.config.padV)
	else
		Macaroon.SetPosition(bar)
	end
end

local function round(num, idp)

      local mult = 10^(idp or 0)
      return math.floor(num * mult + 0.5) / mult

end

function updateVisibility(bar, message)

	if (not message) then return end

	if (bar.handler:GetAttribute("isBarChild")) then

		bar.handler:SetAttribute("stateshown", false)

		for showstate in gmatch(bar.handler:GetAttribute("showstates"), "[^;]+") do
			if (message and strfind(message, showstate)) then
				bar.handler:Show()
				bar.handler:SetAttribute("stateshown", true)
			end
		end

		if (not bar.handler:GetAttribute("stateshown")) then
			bar.handler:Hide()
		end
	end
end

local function updateShape(bar, storage)

	if (not bar) then return end

	local width, height, xOffset, yOffset, padH, padV  = 0, 0, 0, 0, bar.config.padH, bar.config.padV
	local shape, x, y, button, count, last, hide = bar.config.shape

	if (bar.config.buttonList) then

		for state, btnIDs in pairs(bar.config.buttonList) do

			local first, pos, count, cAdjust, rAdjust, arcStart, arcLength = false, 1, bar[state].buttonCount, 0.5, 1, bar.config.arcStart, bar.config.arcLength
			local origCol, rows, columns, placed, anchor = bar.config.columns, (round(ceil(count/bar.config.columns), 1)/2)+0.5

			if (storage) then
				anchor = bar
			else
				anchor = bar[state].anchor or getAnchor(bar, state)
			end

			for btnID in gmatch(btnIDs, "[^;]+") do

				button = _G[bar.btnType..btnID]

				if (not button) then
					DEFAULT_CHAT_FRAME:AddMessage(bar.config.buttonList[state])
					DEFAULT_CHAT_FRAME:AddMessage("Macaroon Critical Error Detected: Invalid button on "..bar:GetName()..". Saved Variables reset required! Sorry!", 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
					break
				end

				if (button.config.clickAnchor or button.config.mouseAnchor) then
					button:SetParent("UIParent")
				else
					button:SetParent(bar.handler)
				end

				button:ClearAllPoints()

				width = button:GetWidth(); height = button:GetHeight()
				xOffset = button.config.XOffset; yOffset = button.config.YOffset

				if (count > origCol and mod(count, origCol)~=0 and rAdjust == 1) then
					columns = (mod(count, origCol))/2
				elseif (origCol >= count) then
					columns = count/2
				else
					columns = origCol/2
				end

				if (shape == 2) then

					if (not placed) then
						placed = arcStart
					end

					x = ((width+padH)*(count/math.pi))*(cos(placed)) / button.config.scale
					y = ((width+padV)*(count/math.pi))*(sin(placed)) / button.config.scale

					button:SetPoint("CENTER", anchor, "CENTER", x + xOffset, y + yOffset)

					placed = placed - (arcLength/count)

				elseif (shape == 3) then

					if (not placed) then

						placed = arcStart

						button:SetPoint("CENTER", anchor, "CENTER", xOffset, yOffset)

						placed = placed - (arcLength/count)

					else

						x = ((width+padH)*(count/math.pi))*(cos(placed)) / button.config.scale
						y = ((width+padV)*(count/math.pi))*(sin(placed)) / button.config.scale

						button:SetPoint("CENTER", anchor, "CENTER", x + xOffset, y + yOffset)

						placed = placed - (arcLength/(count-1))
					end
				else

					if (not placed) then
						placed = 0
					end

					button:SetPoint("CENTER", anchor, "CENTER", -(width + padH) * (columns-cAdjust) + xOffset, (height + padV) * (rows-rAdjust) + yOffset)

					placed = placed + 1; cAdjust = cAdjust + 1

					if (placed >= columns*2) then
						placed = 0
						cAdjust = 0.5
						rAdjust = rAdjust + 1
					end

				end

				button.updateButtonData(button, bar, state)

				if (not storage) then
					bar.btnTable[button.id][2] = 0
				end

				button.config.barPos = pos; pos = pos + 1

				-- Bump the scale up before setting the true scale, it seems to make text elements scale better
				button:SetScale(1)
				button:SetScale(bar.config.scale * button.config.scale)
				--
			end

			if (not storage) then

				bar[state].buttonCount = 0

				bar[state].top = nil; bar[state].bottom = nil; bar[state].left = nil; bar[state].right = nil

				for btnID in gmatch(btnIDs, "[^;]+") do

					button = _G[bar.btnType..btnID]

					local btnTop, btnBottom, btnLeft, btnRight = button:GetTop(), button:GetBottom(), button:GetLeft(), button:GetRight()
					local scale = button.config.scale

					bar[state].buttonCount = bar[state].buttonCount + 1

					--[[
					if (bar[state].top) then
						if (btnTop > bar[state].top) then bar[state].top = btnTop end
					else bar[state].top = btnTop end

					if (bar[state].bottom) then
						if (btnBottom < bar[state].bottom) then bar[state].bottom = btnBottom end
					else bar[state].bottom = btnBottom end

					if (bar[state].left) then
						if (btnLeft < bar[state].left) then bar[state].left = btnLeft end
					else bar[state].left = btnLeft end

					if (bar[state].right) then
						if (btnRight > bar[state].right) then bar[state].right = btnRight end
					else bar[state].right = btnRight end

					]]--

					if (bar[state].top) then
						if (btnTop*scale > bar[state].top) then bar[state].top = btnTop*scale end
					else bar[state].top = btnTop*scale end

					if (bar[state].bottom) then
						if (btnBottom*scale < bar[state].bottom) then bar[state].bottom = btnBottom*scale end
					else bar[state].bottom = btnBottom*scale end

					if (bar[state].left) then
						if (btnLeft*scale < bar[state].left) then bar[state].left = btnLeft*scale end
					else bar[state].left = btnLeft*scale end

					if (bar[state].right) then
						if (btnRight*scale > bar[state].right) then bar[state].right = btnRight*scale end
					else bar[state].right = btnRight*scale end
				end

				if (not bar[state].xOffset) then bar[state].xOffset = 0 end
				if (not bar[state].yOffset) then bar[state].yOffset = 0 end

				if (bar[state].top and bar[state].bottom and bar[state].left and bar[state].right) then

					local width, height = ((bar[state].right-bar[state].left)*bar.config.scale)/2, ((bar[state].top-bar[state].bottom)*bar.config.scale)/2
					local x, y = bar:GetCenter()
					local top, bottom, left, right = bar[state].top-((y+height)/bar.config.scale), bar[state].bottom-((y-height)/bar.config.scale), bar[state].left-((x-width)/bar.config.scale), bar[state].right-((x+width)/bar.config.scale)

					bar[state].xOffset = bar[state].xOffset - (left+right)/2
					bar[state].yOffset = bar[state].yOffset - (top+bottom)/2
				end

				anchor:SetPoint("CENTER", bar, "CENTER", bar[state].xOffset, bar[state].yOffset)
			end
		end
	end
end

local function updateBarSize(bar)

	local currState = bar.handler:GetAttribute("state-current")

	if (bar[currState] and bar[currState].buttonCount and bar[currState].buttonCount > 0 and bar[currState].right) then
		bar:SetWidth((bar[currState].right-bar[currState].left)*bar.config.scale)
		bar:SetHeight((bar[currState].top-bar[currState].bottom)*bar.config.scale)
	else
		bar:SetWidth(195)
		bar:SetHeight(30)
	end
end

local function updateBarLink(bar)

	local handler = bar.handler

	if (bar.config.barLink and bar.config.showstates) then

		local parent = Macaroon.BarIndexByName[bar.config.barLink].handler

		handler:SetParent(parent:GetName())
		handler:SetAttribute("isBarChild", true)

		for k,v in pairs(MACAROON_STRINGS.STATES) do

			if (v == bar.config.showstates) then

				local state = k

				for kk,vv in pairs(managedStates) do
					if (k == vv.homestate) then
						state = "homestate"
					end
				end

				handler:SetAttribute("showstates", state)
			end
		end

		bar.updateVisibility(bar, parent:GetAttribute("state-current"))

	elseif (not bar.config.barLink and not bar.config.showstates) then

		handler:SetParent("UIParent")
		handler:SetAttribute("isBarChild", "nil")

		handler:SetAttribute("showstates", "homestate")

		bar.updateVisibility(bar, "homestate")
	end
end

local function updateBarTarget(bar, show)

	--fix for a change in how value is used
	if (bar.config.target == "none") then
		bar.config.target = false
	end

	if (show or bar:IsVisible()) then

		bar.handler:SetAttribute("unit", nil)
		UnregisterUnitWatch(bar.handler)
	else

		local target = bar.config.target

		if (target and not bar.editmode) then
			bar.handler:SetAttribute("unit", target)
			RegisterUnitWatch(bar.handler)
		else
			bar.handler:SetAttribute("unit", nil)
			UnregisterUnitWatch(bar.handler)
		end
	end
end

local function updateBarHidden(bar, show, hide)

	local isAnchorChild = bar.handler:GetAttribute("isAnchorChild")

	if (not hide and not isAnchorChild and (show or bar:IsVisible())) then

		bar.handler:Show()
	else
		if (bar.config.hidden) then
			bar.handler:Hide()
		elseif (not bar.config.barLink and not isAnchorChild) then
			bar.handler:Show()
		end
	end
end

local function addStates(bar, handler, state, conditions)

	if (bar.config.target) then
		conditions = gsub(conditions, "target=%P+", "target="..bar.config.target)
	end

	RegisterStateDriver(handler, state, conditions)

	if (managedStates[state].homestate) then
		handler:SetAttribute("state-homestate", managedStates[state].homestate)
	end

	bar[state] = { registered = true }

	bar.updateBar(bar, nil, true, true)
end

local function clearStates(bar, handler, state, start, stop)

	local clearState

	if (state == "homestate") then

		if (bar[state] and bar[state].buttonCount and bar[state].buttonCount > 0) then
			Macaroon.RemoveButton(bar[state].buttonCount, bar, state)
		end

	elseif (state) then

		for i=start,stop do
			clearState = state..i
			if (bar[clearState] and bar[clearState].buttonCount and bar[clearState].buttonCount > 0) then
				Macaroon.RemoveButton(bar[clearState].buttonCount, bar, clearState)
			end
		end

		if (managedStates[state].homestate == handler:GetAttribute("state-homestate")) then
			handler:SetAttribute("state-homestate", nil)
		end

		UnregisterStateDriver(handler, state)

		handler:SetAttribute(state.."-homestate", nil)

		bar[state] = { registered = false }
	end

	bar.updateBar(bar, nil, true, true)
end

local function updateBarSelfFocusCast(handler)

	handler:SetAttribute("alt-unit*", nil)
	handler:SetAttribute("ctrl-unit*", nil)
	handler:SetAttribute("shift-unit*", nil)

	if (not ss.selfCast) then return end

	local modifier = lower(match(ss.selfCast, "^%a+"))

	handler:SetAttribute(modifier.."-unit*", "player")

	if (not ss.focusCast) then return end

	modifier = lower(match(ss.focusCast, "^%a+"))

	handler:SetAttribute(modifier.."-unit*", "focus")
end

local function updateRightCast(handler)

	handler:SetAttribute("alt-unit2", nil)
	handler:SetAttribute("ctrl-unit2", nil)
	handler:SetAttribute("shift-unit2", nil)
	handler:SetAttribute("unit2", nil)

	if (not ss.rightClickTarget) then return end

	handler:SetAttribute("alt-unit2", ss.rightClickTarget)
	handler:SetAttribute("ctrl-unit2", ss.rightClickTarget)
	handler:SetAttribute("shift-unit2", ss.rightClickTarget)
	handler:SetAttribute("unit2", ss.rightClickTarget)
end

local function buildStateMap(bar, remapState)

	local statemap, state, map, remap, homestate = "", gsub(remapState, "pagedbar", "bar")

	for states in gmatch(bar.config.remap, "[^;]+") do

		map, remap = (":"):split(states)

		if (not homestate) then
			statemap = statemap.."["..state..":"..map.."] homestate; "
			homestate = true
		else
			local newstate = remapState..remap

			if (managedStates[remapState] and
			    managedStates[remapState].homestate and
			    managedStates[remapState].homestate == newstate) then
				statemap = statemap.."["..state..":"..map.."] homestate; "
			else
				statemap = statemap.."["..state..":"..map.."] "..newstate.."; "
			end
		end

		if (map == "1" and bar.config.prowl and remapState == "stance") then
			statemap = statemap.."[stance:2/3,stealth] stance8; "
		end
	end

	statemap = gsub(statemap, "; $", "")

	return statemap
end

local function updateBarOptions(bar)

	local handler, currState, data, start, stop = bar.handler

	if (bar.config.autoHide) then
		autoHideIndex[bar] = handler
	else
		autoHideIndex[bar] = nil
	end

	if (bar.config.alphaUp == MACAROON_STRINGS.ALPHAUP_NONE) then
		alphaupIndex[bar] = nil
	else
		alphaupIndex[bar] = handler
	end

	if (not bar.config.snapTo) then
		for k,v in pairs(Macaroon.BarIndex) do
			if (v.config.snapToFrame == bar:GetName()) then
				v.config.snapToFrame = false
				v.config.snapToPoint = false
				v.config.point, v.config.x, v.config.y = Macaroon.GetPosition(v)
				Macaroon.SetPosition(v)
			end
		end
	end

	if (bar.stateschanged) then

		for state, values in pairs(managedStates) do

			if (bar.config[state]) then

				if (not bar[state] or not bar[state].registered) then

					local statemap

					if (bar.config.remap) then
						if (state == "pagedbar" or state == "stance") then
							statemap = buildStateMap(bar, state)
						end
					end

					if (state == "custom" and bar.config.custom) then

						addStates(bar, handler, state, bar.config.custom)

					elseif (statemap) then

						addStates(bar, handler, state, statemap)

					else
						addStates(bar, handler, state, values.states)

					end
				end
			else

				if (bar[state] and bar[state].registered) then

					if (state == "custom" and bar.config.customRange) then

						local start = tonumber(match(bar.config.customRange, "^%d+"))
						local stop = tonumber(match(bar.config.customRange, "%d+$"))

						if (start and stop and bar[state] and bar[state].registered) then
							clearStates(bar, handler, state, start, stop)
						end

					else

						clearStates(bar, handler, state, values.rangeStart, values.rangeStop)

					end
				end
			end
		end
	end

	updateBarSelfFocusCast(handler)

	updateRightCast(handler)

	updateBarLink(bar)

	updateBarTarget(bar)

	bar.stateschanged = nil

	handler:SetAlpha(bar.config.alpha)

	bar:SetFrameStrata(bar.config.barStrata)
end

local function updateBar(bar, options, shape, size, pos)

	bar.elapsed = 0

	if (not bar.handler:GetAttribute("state-current")) then
		bar.handler:SetAttribute("state-current", "homestate")
	end

	if (not bar.handler:GetAttribute("state-last")) then
		bar.handler:SetAttribute("state-last", "homestate")
	end

	if (options) then updateBarOptions(bar) end

	if (bar.config.buttonList) then
		for state, btnIDs in pairs(bar.config.buttonList) do

			if (not bar[state]) then
				bar[state] = {}
			end

			bar[state].buttonCount = 0

			for btnID in gmatch(btnIDs, "[^;]+") do
				bar[state].buttonCount = bar[state].buttonCount + 1
			end
		end
	end

	if (not bar.config.barLink and not bar.handler:GetAttribute("isAnchorChild")) then
		bar.handler:SetParent("UIParent")
	end

	if (shape) then updateShape(bar) end

	if (size) then updateBarSize(bar) end

	if (pos) then setPosition(bar) end

	updateBarHidden(bar)

	local state = bar.handler:GetAttribute("state-current")

	if (state and bar.config.custom and bar.config.customNames and bar.config.customNames[state]) then

		bar.text:SetText(bar.config.name.." - "..bar.config.customNames[state])

	elseif (state and MACAROON_STRINGS.STATES[state]) then

		if (state == "homestate") then

			if (bar.pagedbar and bar.pagedbar.registered) then

				bar.text:SetText(bar.config.name.." - "..MACAROON_STRINGS.STATES.pagedbar1)

			elseif (bar.stance and bar.stance.registered) then

				if (UnitClass("player") == MACAROON_STRINGS.WARRIOR) then

					local stanceState = MACAROON_STRINGS.STATES.stance1 or MACAROON_STRINGS.STATES[state]

					if (stanceState) then
						bar.text:SetText(bar.config.name.." - "..stanceState)
					else
						bar.text:SetText(bar.config.name.." - ")
					end
				else
					local stanceState = MACAROON_STRINGS.STATES.stance0 or MACAROON_STRINGS.STATES[state]

					if (stanceState) then
						bar.text:SetText(bar.config.name.." - "..stanceState)
					else
						bar.text:SetText(bar.config.name.." - ")
					end
				end
			elseif (bar.pet and bar.pet.registered) then

				bar.text:SetText(bar.config.name.." - "..MACAROON_STRINGS.STATES.pet1)

			else
				bar.text:SetText(bar.config.name.." - "..MACAROON_STRINGS.STATES[state])
			end
		else
			bar.text:SetText(bar.config.name.." - "..MACAROON_STRINGS.STATES[state])
		end

	elseif (state) then

		bar.text:SetText(bar.config.name.." - "..state.." (not indexed)")

	else
		bar.text:SetText(bar.config.name.." - (unidentified - oops)")
	end

	if (GameTooltip:IsVisible() and bar:IsVisible() and MouseIsOver(bar)) then
		Macaroon.Bar_OnEnter(bar)
	end

	Macaroon.SkinBar(bar)

	Macaroon.AddonSupport("DrDamage")

	for k,v in pairs(Macaroon.StatesToSave) do v() end

	clearTable(Macaroon.BarIndexByName)

	for k,bar in pairs(Macaroon.BarIndex) do
		Macaroon.BarIndexByName[bar.config.name] = bar
	end
end

local function barDefaults(index, bar)

	bar.config = {

		name = "Bar "..index,
		buttonStrata = "LOW",
		barStrata = "MEDIUM",
		scale = 1,
		alpha = 1,
		alphaUp = "none",
		shape = 1,
		arcStart = 90,
		arcLength = 360,
		columns = 12,
		padH = 0,
		padV = 0,

		currentstate = "homestate",
		laststate = "homestate",

		barLink = false,
		showstates = "",

		point = "CENTER",
		x = 0,
		y = 0,

		snapToPoint = false,
		snapToFrame = false,

		target = false,
		hidden = false,

		homestate = true,
		pagedbar = false,
		stance = false,
		stealth = false,
		reaction = false,
		combat = false,
		group = false,
		pet = false,
		possess = false,
		alt = false,
		ctrl = false,
		shift = false,
		prowl = false,

		custom = false,
		customRange = false,
		customNames = false,

		remap = false,

		snapTo = false,
		autoHide = false,
		showGrid = false,

		hotKeys = "",
		hotKeyText = "",

		buttonList = {
			homestate = "",
		},

		visuals = {

			color1 = { 1, 1, 1 },
			color2 = { 0.1, 0.1, 1 },
			color3 = { 0.1, 1, 0.1 },
			color4 = { 0.7, 0.15, 0.15 },
			color5 = { 0.2, 0.3, 0.7 },
			color6 = { 0.0, 0.8, 0.0 },
			color7 = { 0.8, 0.0, 0.0 },
			color8 = { 1, 0.82, 0 },
			color9 = { 1, 0.1, 0.1 },
			color10 = { 0, 0.82, 0 },
			color11 = { 1, 0.1, 0.1 },

			setpoint1 = { -2, 9, "RIGHT", "CENTER" },
			setpoint2 = { -2, -9, "RIGHT", "CENTER" },
			setpoint3 = { 0, -10, "CENTER", "CENTER" },
			setpoint4 = { 0, 0, "CENTER", "CENTER" },
			setpoint5= { 0, 0, "CENTER", "CENTER" },

			hitbox = { 0, 0, 0, 0 },

			dimensions = { 36, 36 },
		},
	}

	bar.elapsed = 3
end

local function updateVariables(element, defaultConfig)

	--if (not element) then return end

	-- Add new vars
	for key,value in pairs(defaultConfig) do

		if (element.config[key] == nil) then

			if (element.config[lower(key)] ~= nil) then

				element.config[key] = element.config[lower(key)]
				element.config[lower(key)] = nil
			else
				element.config[key] = value
			end
		end
	end
	-- Add new vars

	-- Kill old vars
	for key,value in pairs(element.config) do
		if (defaultConfig[key] == nil) then
			element.config[key] = nil
		end
	end
	-- Kill old vars
end

local function createHandler(bar)

	local handler = CreateFrame("Frame", "MacaroonHandler"..bar:GetID(), UIParent, "SecureHandlerStateTemplate,SecureHandlerShowHideTemplate")

	handler:SetAttribute("_onstate-pagedbar", [[

				self:SetAttribute("state-current", self:GetAttribute("state-pagedbar"))

				if (not self:GetAttribute("state-current")) then
					self:SetAttribute("state-current", "homestate")
				end

				control:ChildUpdate("pagedbar", self:GetAttribute("state-current"))

				self:SetAttribute("state-last", self:GetAttribute("state-pagedbar"))

				if (not self:GetAttribute("state-last")) then
					self:SetAttribute("state-last", "homestate")
				end
				]])
	handler:SetAttribute("_onstate-stance", [[

				self:SetAttribute("state-current", self:GetAttribute("state-stance"))

				if (not self:GetAttribute("state-current")) then
					self:SetAttribute("state-current", "homestate")
				end

				control:ChildUpdate("stance", self:GetAttribute("state-current"))

				self:SetAttribute("state-last", self:GetAttribute("state-stance"))

				if (not self:GetAttribute("state-last")) then
					self:SetAttribute("state-last", "homestate")
				end
				]])
	handler:SetAttribute("_onstate-pet", [[

				self:SetAttribute("state-current", self:GetAttribute("state-pet"))

				if (not self:GetAttribute("state-current")) then
					self:SetAttribute("state-current", "homestate")
				end

				control:ChildUpdate("pet", self:GetAttribute("state-current"))

				self:SetAttribute("state-last", self:GetAttribute("state-pet"))

				if (not self:GetAttribute("state-last")) then
					self:SetAttribute("state-last", "homestate")
				end
				]])
	handler:SetAttribute("_onstate-stealth", [[

				if (self:GetAttribute("state-stealth") and strfind(self:GetAttribute("state-stealth"), "laststate") and self:GetAttribute("state-pagedbar")) then

					self:SetAttribute("state-current", self:GetAttribute("state-pagedbar"))
					control:ChildUpdate("stealth", self:GetAttribute("state-current"))
					self:SetAttribute("state-last", self:GetAttribute("state-pagedbar"))

				elseif (self:GetAttribute("state-stealth") and strfind(self:GetAttribute("state-stealth"), "laststate") and self:GetAttribute("state-stance")) then

					self:SetAttribute("state-current", self:GetAttribute("state-stance"))
					control:ChildUpdate("stealth", self:GetAttribute("state-current"))
					self:SetAttribute("state-last", self:GetAttribute("state-stance"))

				elseif (self:GetAttribute("state-stealth") and strfind(self:GetAttribute("state-stealth"), "laststate")) then

					self:SetAttribute("state-current", "homestate")
					control:ChildUpdate("stealth", self:GetAttribute("state-current"))
					self:SetAttribute("state-last", "homestate")
				else
					self:SetAttribute("state-current", self:GetAttribute("state-stealth"))
					control:ChildUpdate("stealth", self:GetAttribute("state-current"))
					self:SetAttribute("state-last", self:GetAttribute("state-stealth"))
				end
				]])
	handler:SetAttribute("_onstate-reaction", [[

				if (self:GetAttribute("state-reaction") and strfind(self:GetAttribute("state-reaction"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("reaction", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-last", self:GetAttribute("state-current"))
					self:SetAttribute("state-current", self:GetAttribute("state-reaction"))
					control:ChildUpdate("reaction", self:GetAttribute("state-current"))
				end
				]])
	handler:SetAttribute("_onstate-combat", [[

				if (self:GetAttribute("state-combat") and strfind(self:GetAttribute("state-combat"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("combat", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-last", self:GetAttribute("state-current"))
					self:SetAttribute("state-current", self:GetAttribute("state-combat"))
					control:ChildUpdate("combat", self:GetAttribute("state-current"))
				end
				]])
	handler:SetAttribute("_onstate-group", [[

				if (self:GetAttribute("state-group") and strfind(self:GetAttribute("state-group"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("group", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-last", self:GetAttribute("state-current"))
					self:SetAttribute("state-current", self:GetAttribute("state-group"))
					control:ChildUpdate("group", self:GetAttribute("state-current"))
				end
				]])
	handler:SetAttribute("_onstate-possess", [[

				if (self:GetAttribute("state-possess") and strfind(self:GetAttribute("state-possess"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("possess", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-last", self:GetAttribute("state-current"))
					self:SetAttribute("state-current", self:GetAttribute("state-possess"))
					control:ChildUpdate("possess", self:GetAttribute("state-current"))
				end
				]])
	handler:SetAttribute("_onstate-alt", [[

				if (self:GetAttribute("state-alt") and strfind(self:GetAttribute("state-alt"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("alt", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-current", self:GetAttribute("state-alt"))
					control:ChildUpdate("alt", self:GetAttribute("state-current"))
				end
				]])

	handler:SetAttribute("_onstate-ctrl", [[

				local self = self
				local control = control

				if (self:GetAttribute("state-ctrl") and strfind(self:GetAttribute("state-ctrl"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("ctrl", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-current", self:GetAttribute("state-ctrl"))
					control:ChildUpdate("ctrl", self:GetAttribute("state-current"))
				end
				]])

	handler:SetAttribute("_onstate-shift", [[

				if (self:GetAttribute("state-shift") and strfind(self:GetAttribute("state-shift"), "laststate")) then
					self:SetAttribute("state-current", self:GetAttribute("state-last"))
					control:ChildUpdate("shift", self:GetAttribute("state-current"))
				else
					self:SetAttribute("state-current", self:GetAttribute("state-shift"))
					control:ChildUpdate("shift", self:GetAttribute("state-current"))
				end
				]])

	handler:SetAttribute("_onstate-custom", [[

				self:SetAttribute("state-last", self:GetAttribute("state-current"))
				self:SetAttribute("state-current", self:GetAttribute("state-custom"))
				control:ChildUpdate("custom", self:GetAttribute("state-current"))
				]])

	handler:SetAttribute("_onshow", [[control:ChildUpdate("onshow", self:GetAttribute("state-current"))]])

	handler:SetAttribute("_childupdate", [[

			if (self:GetAttribute("isBarChild")) then

				self:SetAttribute("stateshown", false)

				for showstate in gmatch(self:GetAttribute("showstates"), "[^;]+") do
					if (message and strfind(message, showstate)) then
						self:Show()
						self:SetAttribute("stateshown", true)
					end
				end

				if (not self:GetAttribute("stateshown")) then
					self:Hide()
				end
			end
		]] )

	handler:SetAttribute("state-current", "homestate")
	handler:SetAttribute("state-last", "homestate")
	handler:SetAttribute("showstates", "homestate")

	handler:HookScript("OnAttributeChanged",
			function(self)

				if (self:GetAttribute("state-current")) then
					self.bar.config.currentstate = self:GetAttribute("state-current")
				else
					self.bar.config.currentstate = "homestate"
				end

				if (self:GetAttribute("state-last")) then
					self.bar.config.laststate = self:GetAttribute("state-last")
				else
					self.bar.config.laststate = "homestate"
				end

				if (self.bar:IsVisible()) then

					if (not InCombatLockdown()) then

						if (not self:GetAttribute("state-current")) then
							self:SetAttribute("state-current", "homestate")
						end

						if (not self:GetAttribute("state-last")) then
							self:SetAttribute("state-last", "homestate")
						end

						updateBar(self.bar, nil, nil, true)
					end
				end
			end)

	handler.bar = bar

	handler:SetPoint("CENTER", bar, "CENTER")

	return handler
end

local function createBar(index, barType, savedState, barTable, btnTable, btnType, btnNew)

	local bar

	if (_G["Macaroon"..barType..index]) then
		bar = _G["Macaroon"..barType..index]

		for state, btnIDs in pairs(bar.config.buttonList) do
			clearTable(bar[state])
		end
	else
	 	bar = CreateFrame("Button", "Macaroon"..barType..index, UIParent, "MacaroonBarTemplate")
	end

	bar:SetID(index)

	barDefaults(index, bar)

	bar.handler = createHandler(bar)

	bar:SetPoint(bar.config.point, bar.config.x, bar.config.y)
	bar.config.point, bar.config.x, bar.config.y = Macaroon.GetPosition(bar)

	bar.updateBar = updateBar
	bar.updateBarLink = updateBarLink
	bar.updateBarHidden = updateBarHidden
	bar.updateBarTarget = updateBarTarget
	bar.updateVisibility = updateVisibility

	bar.hasAction = "Interface\\Buttons\\UI-Quickslot2"
	bar.noAction = "Interface\\Buttons\\UI-Quickslot"

	bar.btnNew = btnNew
	bar.btnTable = btnTable
	bar.btnType = btnType

	if (not savedState[lower(barType)]) then
		savedState[lower(barType)] = {}
	end

	savedState[lower(barType)][index] = { bar.config }

	barTable[index] = bar

	Macaroon.BarIndex[bar:GetName()] = bar

	Macaroon.BarIndexByName[bar.config.name] = bar

	bar.updateBar(bar)

	return bar
end

function Macaroon.DeleteBar(frame)

	local bar = frame

	if (not bar) then
		bar = Macaroon.CurrentBar
	end

	if (bar) then

		local barTable = Macaroon.CreateBarTypes[bar.createmsg][3]

		clearStates(bar, bar.handler, "homestate")

		for state, values in pairs(managedStates) do

			if (bar.config[state] and bar[state] and bar[state].registered) then

				if (state == "custom" and bar.config.customRange) then

					local start = tonumber(match(bar.config.customRange, "^%d+"))
					local stop = tonumber(match(bar.config.customRange, "%d+$"))

					if (start and stop and bar[state] and bar[state].registered) then
						clearStates(bar, bar.handler, state, start, stop)
					end

				else

					clearStates(bar, bar.handler, state, values.rangeStart, values.rangeStop)

				end
			end
		end

		bar:Hide()

		barTable[bar:GetID()] = nil

		Macaroon.BarIndex[bar:GetName()] = nil
		Macaroon.BarIndexByName[bar.config.name] = nil

		for k,v in pairs(Macaroon.StatesToSave) do v() end
	end
end

function Macaroon.CreateBar(msg, load, useDefaults)

	local index, barType, savedState, barTable, btnTable, btnType, btnNew, defaults

	if (Macaroon.CreateBarTypes[msg]) then

		barType = Macaroon.CreateBarTypes[msg][1]
		savedState = Macaroon.CreateBarTypes[msg][2]
		barTable = Macaroon.CreateBarTypes[msg][3]
		btnTable = Macaroon.CreateBarTypes[msg][4]
		btnType = Macaroon.CreateBarTypes[msg][5]
		btnNew = Macaroon.CreateBarTypes[msg][6]

		if (useDefaults) then defaults = Macaroon.CreateBarTypes[msg][7] end

	else
		local data, index, high = {}, 1, 0

		for k,v in pairs(Macaroon.CreateBarTypes) do
			index = tonumber(match(v[8], "%d+"))
			data[index] = { k, v[8] }
			if (index > high) then high = index end
		end

		for i=1,high do	if (not data[i]) then data[i] = 0 end end

		print("Usage: /mac create <type>\n")
		print("     Types -\n")

		for k,v in ipairs(data) do
			if (type(v) == "table") then
				print("       |cff00ff00"..v[1].."|r: "..gsub(v[2],"%d+",""))
			end
		end

		return
	end

	index = #barTable + 1

	if (not index) then index = 1 end

	local bar = createBar(index, barType, savedState, barTable, btnTable, btnType, btnNew)

	bar.createmsg = msg

	if (defaults) then defaults(bar) end

	bar:SetWidth(195)
	bar:SetHeight(30)

	if (not load) then
		Macaroon.ChangeBar(bar)
		Macaroon.ConfigBars(nil, true)
	end

	return bar
end

function Macaroon.CreateNewBar(msg)
	Macaroon.CreateBar(msg, nil, true)
end

function Macaroon.AutohideBar(command, gui, checked)

	local bar = Macaroon.CurrentBar

	if (bar) then

		local toggle = bar.config.autoHide

		if (gui) then
			if (checked) then
				toggle = false
			else
				toggle = true
			end
		end

		if (toggle) then
			bar.config.autoHide = false
			bar.handler:SetAlpha(1)
		else
			bar.config.autoHide = true
		end

		bar.updateBar(bar, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.ShowgridSet(command, gui, checked)

	local bar = Macaroon.CurrentBar

	if (bar) then

		local toggle = bar.config.showGrid

		if (gui) then
			if (checked) then
				toggle = false
			else
				toggle = true
			end
		end

		if (toggle) then
			bar.config.showGrid = false
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_SHOWGRID_DISABLED..Macaroon.CurrentBar.config.name, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
		else
			bar.config.showGrid = true
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_SHOWGRID_ENABLED..Macaroon.CurrentBar.config.name, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
		end

		bar.updateBar(bar, nil, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.SnapToBar(command, gui, checked)

	local bar = Macaroon.CurrentBar

	if (bar) then

		local toggle = bar.config.snapTo

		if (gui) then
			if (checked) then
				toggle = false
			else
				toggle = true
			end
		end

		if (toggle) then
			bar.config.snapTo = false
			bar.config.snapToPoint = false
			bar.config.snapToFrame = false
			bar:SetUserPlaced(true)
			bar.config.point, bar.config.x, bar.config.y = Macaroon.GetPosition(bar)
			Macaroon.SetPosition(bar)
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_SNAPTO_DISABLED..Macaroon.CurrentBar.config.name, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
		else
			bar.config.snapTo = true
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_SNAPTO_ENABLED..Macaroon.CurrentBar.config.name, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
		end

		bar.updateBar(bar, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.HideBar(command, gui, checked)

	if (InCombatLockdown()) then return end

	local bar

	if (command) then

		bar = Macaroon.BarIndexByName[command]

		if (not bar) then
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			return
		end
	else
		bar = Macaroon.CurrentBar
	end

	if (bar) then

		local toggle = bar.config.hidden

		if (gui) then
			if (checked) then
				toggle = false
			else
				toggle = true
			end
		end

		if (toggle) then
			bar.config.hidden = false
			if (bar.selected) then
				bar:SetBackdropColor(0,0,1,0.4)
			else
				bar:SetBackdropColor(0,0,0,0.2)
			end
			if (not command) then
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_HIDDEN_DISABLED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		else
			bar.config.hidden = true
			if (bar.selected) then
				bar:SetBackdropColor(1,0,0,0.4)
			else
				bar:SetBackdropColor(0.75,0,0,0.2)
			end
			if (not command) then
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.BAR_HIDDEN_ENABLED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end

		bar.updateBar(bar, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.ScaleBar(scale)

	local bar = Macaroon.CurrentBar

	if (bar) then

		bar.config.scale = scale
		bar.updateBar(bar, true, true, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.ShapeBar(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_SHAPES)
		else

			local shape = tonumber(command)

			if (shape) then
				bar.config.shape = shape
				bar.updateBar(bar, true, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.NameBar(name)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (name) then
			bar.config.name = name
			bar.updateBar(bar)
		else
			MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.StrataSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_STRATAS)
		else

			local strata = tonumber(command)

			if (strata and strata>0 and strata<#stratas) then

				bar.config.buttonStrata = stratas[strata]
				bar.config.barStrata = stratas[strata+1]
				bar.updateBar(bar, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.AlphaSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_ALPHA)
		else

			local alpha = tonumber(command)

			if (alpha and alpha>=0 and alpha<=1) then

				bar.config.alpha = alpha
				bar.updateBar(bar, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.AlphaUpSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then

			local text = ""

			for k,v in ipairs(alphaUps) do
				text = text.."\n"..k.."="..v
			end

			DEFAULT_CHAT_FRAME:AddMessage(text)
		else

			local alphaUp = tonumber(command)

			if (alphaUp and alphaUp>0 and alphaUp<#alphaUps+1) then

				bar.config.alphaUp = alphaUps[alphaUp]
				bar.updateBar(bar, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.ArcStartSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_ARCSTART)
		else

			local start = tonumber(command)

			if (start and start>=0 and start<=359) then

				bar.config.arcStart = start
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.ArcLengthSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_ARCLENGTH)
		else

			local length = tonumber(command)

			if (length and length>=0 and length<=359) then

				bar.config.arcLength = length
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end
end

function Macaroon.ColumnsSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_COLUMNS)
		else

			local columns = tonumber(command)

			if (columns and columns>0) then

				bar.config.columns = columns
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.PadHSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_PADH)
		else

			local padh = tonumber(command)

			if (padh) then

				bar.config.padH = padh
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.PadVSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_PADV)
		else

			local padv = tonumber(command)

			if (padv) then

				bar.config.padV = padv
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.PadHVSet(command)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not command) then
			DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.BAR_PADHV)
		else

			local padhv = tonumber(command)

			if (padhv) then

				bar.config.padH = bar.config.padH + padhv
				bar.config.padV = bar.config.padV + padhv
				bar.updateBar(bar, nil, true, true)
			else
				MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.INVALID_CHOICE, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
			end
		end
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.XAxisSet(x)

	local bar = Macaroon.CurrentBar

	if (bar) then

		bar.config.x = bar.config.x + tonumber(x)

		Macaroon.SetPosition(bar)

		bar.config.point, bar.config.x, bar.config.y = Macaroon.GetPosition(bar)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.YAxisSet(y)

	local bar = Macaroon.CurrentBar

	if (bar) then

		bar.config.y = bar.config.y + tonumber(y)

		Macaroon.SetPosition(bar)

		bar.config.point, bar.config.x, bar.config.y = Macaroon.GetPosition(bar)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.SetBarStates(msg, gui, silent)

	local bar = Macaroon.CurrentBar

	if (bar) then

		if (not msg) then
			if (not silent) then
				Macaroon.PrintStateList()
				return
			end
		end

		local state = match(msg, "^%S+")
		local command = gsub(msg, state, ""); command = gsub(command, "^%s+", "")

		if (not managedStates[state]) then
			if (not silent) then
				Macaroon.PrintStateList()
				return
			end
		end

		if (bar.config[state] and not gui) then

			bar.config[state] = false

		elseif (not gui) then

			bar.config[state] = true
		end

		if (state == "pagedbar") then

			bar.config.stance = false
			bar.config.pet = false

			if (bar.config.pagedbar) then
				bar.config.remap = ""
				for i=1,6 do
					bar.config.remap = bar.config.remap..i..":"..i..";"
				end
				bar.config.remap = gsub(bar.config.remap, ";$", "")
			else
				bar.config.remap = false
			end
		end

		if (state == "stance") then

			bar.config.pagedbar = false

			if (not bar.config.stance and bar.config.prowl) then
				bar.config.prowl = false
			end

			bar.config.pet = false

			if (bar.config.stance) then

				local start = tonumber(match(Macaroon.managedStates.stance.homestate, "%d+"))

				if (start) then
					bar.config.remap = ""
					for i=start,GetNumShapeshiftForms() do

						if (UnitClass("player") == MACAROON_STRINGS.WARLOCK and i == 1) then
							local j = i+1
							bar.config.remap = bar.config.remap..j..":"..i..";"
						else
							bar.config.remap = bar.config.remap..i..":"..i..";"
						end
					end
					bar.config.remap = gsub(bar.config.remap, ";$", "")
				end
			else
				bar.config.remap = false
			end
		end

		if (state == "custom") then

			if (bar.config.custom) then

				local count, newstates = 0, ""

				bar.config.customNames = {}

				for states in gmatch(command, "[^;]+") do

					bar.config.customRange = "1;"..count

					if (count == 0) then
						newstates = states.." homestate; "
						bar.config.customNames["homestate"] = states
					else
						newstates = newstates..states.." custom"..count.."; "
						bar.config.customNames["custom"..count] = states
					end

					count = count + 1
				end

				bar.config.custom = newstates or ""

			else
				bar.config.customNames = false
				bar.config.customRange = false
			end
		end

		if (state == "pet") then
			bar.config.pagedbar = false
			bar.config.stance = false
		end

		bar.stateschanged = true

		bar.updateBar(bar, true)
	else
		MacaroonMessageFrame:AddMessage(MACAROON_STRINGS.NO_BAR_SELECTED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
	end

end

function Macaroon.PrintStateList()

	local data, list = {}

	for k,v in pairs(managedStates) do
		data[v.printOrder] = k
	end

	for k,v in ipairs(data) do

		if (not list) then
			list = "\n|cff00ff00Valid states:|r "..v
		else
			list = list..", "..v
		end
	end

	DEFAULT_CHAT_FRAME:AddMessage(list..MACAROON_STRINGS.CUSTOM_OPTION)
end

function Macaroon.GetPosition(frame)

	local parent, point = frame:GetParent()
	local s = frame:GetScale()
	local w, h = parent:GetWidth()/s, parent:GetHeight()/s
	local x, y = frame:GetCenter()
	local vert = (y>h/1.5) and "TOP" or (y>h/3) and "CENTER" or "BOTTOM"
	local horz = (x>w/1.5) and "RIGHT" or (x>w/3) and "CENTER" or "LEFT"

	if (vert == "CENTER") then
		point = horz
	elseif (horz == "CENTER") then
		point = vert
	else
		point = vert..horz
	end

	if (find(vert, "CENTER")) then y = y - h/2 end
	if (find(horz, "CENTER")) then x = x - w/2 end
	if (find(point, "RIGHT")) then x = x - w end
	if (find(point, "TOP")) then y = y - h end

	return point, x, y
end

function Macaroon.SetPosition(frame)

	local point, x, y = frame.config.point, frame.config.x, frame.config.y

	frame:SetUserPlaced(false)
	frame:ClearAllPoints()
	frame:SetPoint("CENTER", "UIParent", point, x, y)
	frame:SetUserPlaced(true)
end

function Macaroon.UpdateBarPositions(table, reset, y)

	if (reset) then

		for k,bar in pairs(table) do
			y = setDefaultPosition(bar, y)
		end
	else
		for k,bar in pairs(table) do
			setPosition(bar)
		end
	end

	return y
end

function Macaroon.GetBarDefaults()

	local defaults = {}

	barDefaults(0, defaults)

	return defaults.config
end

function Macaroon.LoadSavedData(saved)

	if (saved) then

		load = true

		local savedState = copyTable(saved)
		local defaultConfig

		if (savedState.bars) then

			clearTable(Macaroon.ButtonBars)

			defaultConfig = Macaroon.GetBarDefaults()

			for k,v in pairs(savedState.bars) do

				local bar = Macaroon.CreateBar("bar", true)

				bar.config = copyTable(savedState.bars[k][1])

				updateVariables(bar, defaultConfig)

				bar.handler:SetAttribute("state-current", bar.config.currentstate)

				bar.handler:SetAttribute("state-last", bar.config.laststate)
			end

			savedState.bars = nil
		end

		if (savedState.buttons) then

			for k,v in pairs(Macaroon.Buttons) do
				Macaroon.StoreButton(v[1], Macaroon.Buttons)
			end

			defaultConfig = Macaroon.GetButtonDefaults()

			for k,v in pairs(savedState.buttons) do

				local button = Macaroon.CreateButton(k)

				button.config = copyTable(savedState.buttons[k][1])

				if (shared and css and css.buttons and css.buttons[k]) then

					local notShared = copyTable(css.buttons[k][1])

					for key in pairs(notSharedButtonData) do
						button.config[key] = notShared[key]
					end
				end

				updateVariables(button, defaultConfig)
			end

			savedState.buttons = nil
		end

		for k,v in pairs(savedState) do
			if (ss[k]) then
				ss[k] = v
			end
		end

		--temp fix for WotLK beta, SetSpell() error in Blizzard feedback UI
		if(g_FeedbackUI_feedbackVars and g_FeedbackUI_feedbackVars["verbose"]) then
			g_FeedbackUI_feedbackVars["verbose"] = false
		end
	end
end

function Macaroon.UpdateElements()

	for k,v in pairs(Macaroon.Buttons) do

		Macaroon.ApplyBindings(v[1])

		Macaroon.SetButtonType(v[1], nil, true)

		Macaroon.SetAnchor(v[1])
	end

	for k,v in pairs(Macaroon.ButtonBars) do

		v.stateschanged = true

		v.buttonCountChanged = true

		v.updateBar(v, true, true, true, true)
	end

	for k,v in pairs(Macaroon.ButtonBars) do

		v.updateBarTarget(v)

		v.updateBarLink(v)

		v.updateBarHidden(v, nil, true)

	end
end

function Macaroon.SaveCurrentState()

	if (load) then

		if (ss.bars) then
			clearTable(ss.bars)
		else
			ss.bars = {}
		end

		for k,v in pairs(Macaroon.ButtonBars) do
			ss.bars[k] = { v.config }
		end

		if (ss.buttons) then
			clearTable(ss.buttons)
		else
			ss.buttons = {}
		end

		for k,v in pairs(Macaroon.Buttons) do
			ss.buttons[k] = { v[1].config }
		end

		if (shared and css and playerEnteredWorld) then

			if (css.buttons) then
				clearTable(css.buttons)
			else
				css.buttons = {}
			end

			for k,v in pairs(Macaroon.Buttons) do

				css.buttons[k] = {}
				css.buttons[k][1] = {}

				for key in pairs(notSharedButtonData) do
					css.buttons[k][1][key] = v[1].config[key]
				end
			end
		end
	end

	return ss, "bars", "buttons"
end

function Macaroon.ButtonStorage_BuildOptions(self)

	local dock, lastDock

	for i=1,12 do

		dock = CreateFrame("Frame", self:GetName().."Dock"..i, self, "MacaroonDockTemplate")

		dock:SetID(i)
		dock:SetFrameLevel(self:GetParent():GetFrameLevel()+2)
		dock:SetWidth(435)
		dock:SetHeight(38)
		dock:SetScale(0.75)
		dock:Show()

		dock.config = Macaroon.GetBarDefaults()
		dock.config.barStrata = "TOOLTIP"
		dock.config.buttonStrata = "DIALOG"
		dock.config.showGrid = true
		dock.handler = dock
		dock.homestate = {}
		dock.btnType = "MacaroonButton"
		dock.hasAction = "Interface\\Buttons\\UI-Quickslot2"
		dock.noAction = "Interface\\Buttons\\UI-Quickslot"

		if (not lastDock) then
			dock:SetPoint("TOPLEFT",22,-22)
			lastDock = dock
		else
			dock:SetPoint("TOPLEFT", lastDock, "BOTTOMLEFT", 0, 0)
			lastDock = dock
		end
	end

end

function Macaroon.UpdateButtonStorage()

	local count, list, dock, num = 0, "", nil, 1

	for index,button in ipairs(Macaroon.Buttons) do

		if (button[2] == 1) then
			button[1]:Show()
			list = list..button[1].id..";"
			count = count + 1
		end

		if (count >= 12) then

			dock = _G["MacaroonButtonStorageDock"..num]

			if (dock) then

				dock.config.buttonList.homestate = list
				dock.homestate.buttonCount = count

				list = ""; num = num + 1; count = 0

				updateShape(dock, true)
			end
		end
	end

	dock = _G["MacaroonButtonStorageDock"..num]

	if (dock) then

		dock.config.buttonList.homestate = list
		dock.homestate.buttonCount = count

		updateShape(dock, true)
	end

end

function Macaroon.AddonSupport(addon)

	if (addon and IsAddOnLoaded(addon)) then
		if (addon == "DrDamage") then

		end
	end

end

function Macaroon.BarsSetSaved()

	if (MacaroonSavedState.useShared and MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]) then

		ss = MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]
		css = MacaroonSavedState
		shared = true

		if (not ss) then
			ss = MacaroonSavedState
			shared = false
		end
	else
		ss = MacaroonSavedState
		shared = false
	end

	return ss
end

local function controlOnEvent(self, event, ...)

	if (event == "VARIABLES_LOADED") then

		managedStates = Macaroon.managedStates

		Macaroon.BarsSetSaved()

		Macaroon.CreateBarTypes.bar = { "Bars", ss, Macaroon.ButtonBars, Macaroon.Buttons, "MacaroonButton", Macaroon.AddNewButton, nil, "1Standard bar" }

		Macaroon.StatesToSave.bars = Macaroon.SaveCurrentState
		Macaroon.SavedDataLoad.bars = Macaroon.LoadSavedData
		Macaroon.SavedDataUpdate.bars = Macaroon.UpdateElements
		Macaroon.SetSavedVars.bars = Macaroon.BarsSetSaved

	elseif (event == "PLAYER_LOGIN") then

		Macaroon.LoadSavedData(ss)

	elseif (event == "PLAYER_ENTERING_WORLD" and not playerEnteredWorld) then

		Macaroon.UpdateElements()

		Macaroon.UpdateButtonStorage()

		playerEnteredWorld = true

	elseif (event == "MEETINGSTONE_CHANGED" and not meetingStoneChanged) then

		--hack fix
		for k,v in pairs(Macaroon.BarIndex) do
			v:Hide()
		end

		meetingStoneChanged = true
	end
end

local frame = CreateFrame("Frame", nil, UIParent)
frame:SetScript("OnEvent", controlOnEvent)
frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("MEETINGSTONE_CHANGED")